package org.yd.BaiduPing.task;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.transaction.Transactional;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.yd.BaiduPing.entity.TArticle;
import org.yd.BaiduPing.entity.TChatper;
import org.yd.BaiduPing.service.ArticleRepository;
import org.yd.BaiduPing.service.ChapterRepository;

@Component
public class ScheduledPingTasks {

    private static final Logger log = LoggerFactory.getLogger(ScheduledPingTasks.class);

    @Value("${baidu_moblie_url}")
    private String baiduMoblieUrl;

    @Value("${baidu_pc_url}")
    private String baiduPcUrl;

    @Value("${mobile_url}")
    private String mobileUrl;

    @Value("${pc_url}")
    private String pcUrl;

    @Value("${info_url}")
    private String infoUrl;

    @Value("${reader_url}")
    private String readerUrl;

    @Value("${ping_info_url}")
    private boolean pingInfoUrl;

    @Value("${ping_reader_url}")
    private boolean pingReaderUrl;

    @Value("${article_num_per_time}")
    private Integer articleNumPerTime;

    @Value("${chapter_num_per_time}")
    private Integer chapterNumPerTime;

    @Value("${enable_pinyin}")
    private boolean enablePinyin;

    @Value("${connnet_timeout}")
    private Integer connnetTimeout;

    @Value("${read_timeout}")
    private Integer readTimeout;

    @Autowired
    private ArticleRepository articleRepository;

    @Autowired
    private ChapterRepository chapterRepository;

    private static final String STR_START_ARTICLE_NO = "start_article_no";
    private static final String STR_START_CHAPTER_NO = "start_chapter_no";

    private Map<Integer, TArticle> articleMap = new HashMap<>();

    /**
     * ping周期，单位分
     */

    @Scheduled(fixedDelay = 180 * 1000)
    @Transactional
    public void BaiduPing() {
        log.info("going to ping baidu.");
        Properties properties = new Properties();
        try {
            properties.load(new FileInputStream("conf.properties"));
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            return;
        }
        List<TArticle> articleList = null;
        List<TChatper> chapterList = null;
        int startArticleNo = Integer.parseInt(properties.getProperty(STR_START_ARTICLE_NO, "0"));
        int startChapterNo = Integer.parseInt(properties.getProperty(STR_START_CHAPTER_NO, "0"));
        int finishArticleNo = startArticleNo;
        int finishChapterNo = startChapterNo;

        if (enablePinyin) {
            Iterable<TArticle> allArticleList = articleRepository.findAll();
            for (TArticle article : allArticleList) {
                articleMap.put(article.getArticleNo(), article);
            }
        }

        if (pingInfoUrl) {
            Pageable articlePage = new PageRequest(0, articleNumPerTime);

            articleList = articleRepository.fetch(startArticleNo, articlePage);
            if (articleList.size() > 0) {
                // log.info("post data {} {}", baiduMoblieUrl,

                boolean postMobileResult = sendPost(baiduMoblieUrl, createInfoUrl(mobileUrl, articleList));

                // log.info("post data {} {}", baiduPcUrl, createInfoUrl(pcUrl,
                // articleList));
                boolean postPcResult = sendPost(baiduPcUrl, createInfoUrl(pcUrl, articleList));

                // for (TArticle tArticle : articleList) {
                // articleRepository.updateArticleByNo(tArticle.getArticleNo());
                // }
                if (postPcResult && postMobileResult) {
                    finishArticleNo = articleList.get(articleList.size() - 1).getArticleNo();
                }
            }
        }
        if (pingReaderUrl) {
            Pageable chapterPage = new PageRequest(0, chapterNumPerTime);
            chapterList = chapterRepository.fetch(startChapterNo, chapterPage);
            if (chapterList.size() > 0) {
                // log.info("post data {} {}", baiduMoblieUrl,
                // createReaderUrl(mobileUrl, chapterList));
                boolean postMobileResult = sendPost(baiduMoblieUrl, createReaderUrl(mobileUrl, chapterList));

                // log.info("post data {} {}", baiduPcUrl,
                // createReaderUrl(pcUrl,
                // chapterList));
                boolean postPcResult = sendPost(baiduPcUrl, createReaderUrl(pcUrl, chapterList));

                // for (TChatper tChapter : chapterList) {
                // chapterRepository.updateChapterByNo(tChapter.getChapterNo());
                // }
                if (postPcResult && postMobileResult) {
                    finishChapterNo = chapterList.get(chapterList.size() - 1).getChapterNo();
                }
            }
        }

        if (articleList.size() > 0) {
            startArticleNo = articleList.get(articleList.size() - 1).getArticleNo();
        }

        writePro(finishArticleNo, finishChapterNo);

        log.info("ping baidu finished.");
    }

    /**
     * 生成简介页URL
     * 
     * @param siteUrl
     *            网站URL
     * @param articleList
     *            小说列表
     * @return 简介页URL
     */
    private String createInfoUrl(String siteUrl, List<TArticle> articleList) {
        List<String> infoUrlList = new ArrayList<String>();
        for (TArticle tArticle : articleList) {
            infoUrlList
                    .add(siteUrl
                            + StringUtils.replace(
                                    StringUtils.replace(
                                            StringUtils.replace(infoUrl, "{articleno}",
                                                    String.valueOf(tArticle.getArticleNo())),
                                            "{pinyin}", tArticle.getPinyin()),
                                    "{subarticleno}", String.valueOf(tArticle.getArticleNo() / 1000)));
        }
        return StringUtils.join(infoUrlList, "\n");
    }

    /**
     * 生成阅读页URL
     * 
     * @param siteUrl
     *            网站URL
     * @param chapterList
     *            小说列表
     * @return 简介页URL
     */
    private String createReaderUrl(String siteUrl, List<TChatper> chapterList) {
        List<String> readerUrlList = new ArrayList<String>();
        for (TChatper chapter : chapterList) {
            String url = StringUtils
                    .replace(
                            StringUtils.replace(
                                    StringUtils.replace(readerUrl, "{articleno}",
                                            String.valueOf(chapter.getArticleNo())),
                                    "{subarticleno}", String.valueOf(chapter.getArticleNo() / 1000)),
                            "{chapterno}", String.valueOf(chapter.getChapterNo()));

            if (enablePinyin) {
                TArticle article = articleMap.get(chapter.getArticleNo());
                if (article != null)
                    url = StringUtils.replace(url, "{pinyin}", article.getPinyin());
            }
            readerUrlList.add(siteUrl + url);
        }
        return StringUtils.join(readerUrlList, "\n");
    }

    /**
     * 向指定 URL 发送POST方法的请求
     * 
     * @param url
     *            发送请求的 URL
     * @param param
     *            请求参数
     * @return 所代表远程资源的响应结果
     */
    private boolean sendPost(String url, String param) {
        PrintWriter out = null;
        BufferedReader in = null;
        boolean postResult = true;
        String result = "";
        try {
            URL realUrl = new URL(url);
            // 打开和URL之间的连接
            HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection();
            // 设置通用的请求属性
            // conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent", "curl/7.12.1");
            conn.setRequestProperty("Content-Type", "text/plain");
            conn.setRequestProperty("Content-Length", String.valueOf(param.length()));
            // 设置timeout时间
            // conn.setConnectTimeout(connnetTimeout * 1000);
            // conn.setReadTimeout(readTimeout * 1000);

            // 发送POST请求必须设置如下两行
            conn.setDoOutput(true);
            conn.setDoInput(true);
            // 获取URLConnection对象对应的输出流
            out = new PrintWriter(conn.getOutputStream());
            // 发送请求参数
            out.print(param);
            log.debug("post body is {}", param);
            // flush输出流的缓冲
            out.flush();
            int responseCode = conn.getResponseCode();
            if (responseCode != 200) {
                log.error("get a wrong code." + responseCode);
                postResult = false;
            }

            // 定义BufferedReader输入流来读取URL的响应
            in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }

            log.info("ping result is {} {}", url, result);

        } catch (Exception e) {
            postResult = false;
            log.error("send post failed.", e);
        }
        // 使用finally块来关闭输出流、输入流
        finally {
            try {
                if (out != null) {
                    out.close();
                }
                if (in != null) {
                    in.close();
                }
            } catch (IOException ex) {
                log.error("close resource failed.", ex);
            }
        }
        return postResult;
    }

    private void writePro(Integer articleNo, Integer chapterNo) {
        Properties properties = new Properties();
        try {
            properties.setProperty(STR_START_ARTICLE_NO, String.valueOf(articleNo));
            properties.setProperty(STR_START_CHAPTER_NO, String.valueOf(chapterNo));
            properties.store(new FileOutputStream("conf.properties"), "Comments");
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }
}